package com.malamute.core.hibernate.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.malamute.core.common.Page;
import com.malamute.core.hibernate.HibernateBaseDao;

/**
 * @author AceGuan gjybst@163.com
 * @description HibernateDao 实现，为其他dao实现做继承使用
 * @date 2012-2-4 下午03:16:36
 * @version v1.0
 */
@Repository
@Lazy(value = true)
public class HibernateBaseDaoImpl<T extends Serializable, PK extends Serializable>
		extends HibernateDaoSupport implements HibernateBaseDao<T, PK> {

	// 实体类类型（由构造方法自动赋值）
	private Class<T> entityClass;

	@Autowired
	public void setSessionFactoryOverride(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	// 构造方法，根据实例类自动获取实例类类型
	public HibernateBaseDaoImpl() {
		this.entityClass = null;
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			this.entityClass = (Class<T>) p[0];
		}
	}

	// 构造方法，根据实例类自动获取SessionFactory与实体类类型
	public HibernateBaseDaoImpl(SessionFactory sessionFactory,
			Class<T> entityClass) {
		super.setSessionFactory(sessionFactory);
		this.entityClass = entityClass;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param id
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#get(java.io.Serializable)
	 */
	@Override
	public T get(PK id) {
		return (T) getHibernateTemplate().get(entityClass, id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param id
	 * 
	 * @param lock
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#getWithLock(java.io.Serializable,
	 * org.hibernate.LockMode)
	 */
	@Override
	public T getWithLock(PK id, LockMode lockMode) {
		T t = (T) getHibernateTemplate().get(entityClass, id, lockMode);
		if (t != null) {
			this.flush();// 立即刷新，否则锁不会生效
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param id
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#load(java.io.Serializable)
	 */
	@Override
	public T load(PK id) {
		return (T) getHibernateTemplate().load(entityClass, id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param id
	 * 
	 * @param lock
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#loadWithLock(java.io.Serializable,
	 * org.hibernate.LockMode)
	 */
	@Override
	public T loadWithLock(PK id, LockMode lockMode) {
		T t = (T) getHibernateTemplate().load(entityClass, id, lockMode);
		if (t != null) {
			this.flush();// 立即刷新，否则锁不会生效
		}
		return t;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#loadAll()
	 */
	@Override
	public List<T> loadAll() {
		return (List<T>) getHibernateTemplate().loadAll(entityClass);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @see com.my.persistence.Persistence#update(java.io.Serializable)
	 */
	@Override
	public void update(T entity) {
		getHibernateTemplate().update(entity);
		// TODO:为什么这里要flush 才能使用
		this.flush();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @param lock
	 * 
	 * @see com.my.persistence.Persistence#update(java.io.Serializable,
	 * org.hibernate.LockMode)
	 */
	@Override
	public void update(T entity, LockMode lockMode) {
		getHibernateTemplate().update(entity, lockMode);
		this.flush();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @see com.my.persistence.Persistence#save(java.io.Serializable)
	 */
	@Override
	public void save(T entity) {
		getHibernateTemplate().save(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @see com.my.persistence.Persistence#saveOrUpdate(java.io.Serializable)
	 */
	@Override
	public void saveOrUpdate(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param id
	 * 
	 * @see com.my.persistence.Persistence#deleteByKey(java.io.Serializable)
	 */
	@Override
	public void deleteByKey(PK id) {
		this.delete(this.load(id));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param id
	 * 
	 * @param lock
	 * 
	 * @see
	 * com.my.persistence.Persistence#deleteByKeyWithLock(java.io.Serializable,
	 * org.hibernate.LockMode)
	 */
	@Override
	public void deleteByKeyWithLock(PK id, LockMode lockMode) {
		this.deleteWithLock(this.load(id), lockMode);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entities
	 * 
	 * @see com.my.persistence.Persistence#deleteAll(java.util.Collection)
	 */
	@Override
	public void deleteAll(Collection<T> entities) {
		getHibernateTemplate().deleteAll(entities);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#bulkUpdate(java.lang.String)
	 */
	@Override
	public int bulkUpdate(String queryString) {
		return getHibernateTemplate().bulkUpdate(queryString);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @param values
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#bulkUpdate(java.lang.String,
	 * java.lang.Object[])
	 */
	@Override
	public int bulkUpdate(String queryString, Object[] values) {
		return getHibernateTemplate().bulkUpdate(queryString, values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#find(java.lang.String)
	 */
	@Override
	public List find(String queryString) {
		return getHibernateTemplate().find(queryString);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @param paramNames
	 * 
	 * @param values
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#findByNamedParam(java.lang.String,
	 * java.lang.String[], java.lang.Object[])
	 */
	@Override
	public List findByNamedParam(String queryString, String[] paramNames,
			Object[] values) {
		return getHibernateTemplate().findByNamedParam(queryString, paramNames,
				values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @param values
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#iterate(java.lang.String,
	 * java.lang.Object[])
	 */
	@Override
	public Iterator iterate(String queryString, Object[] values) {
		return getHibernateTemplate().iterate(queryString, values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param it
	 * 
	 * @see com.my.persistence.Persistence#closeIterator(java.util.Iterator)
	 */
	@Override
	public void closeIterator(Iterator it) {
		getHibernateTemplate().closeIterator(it);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#createDetachedCriteria()
	 */
	@Override
	public DetachedCriteria createDetachedCriteria() {
		return DetachedCriteria.forClass(this.entityClass);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#createCriteria()
	 */
	@Override
	public Criteria createCriteria() {
		return this.createDetachedCriteria().getExecutableCriteria(
				this.getSession());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param criteria
	 * 
	 * @return
	 * 
	 * @see
	 * com.my.persistence.Persistence#findByCriteria(org.hibernate.criterion
	 * .DetachedCriteria)
	 */
	@Override
	public List findByCriteria(DetachedCriteria criteria) {
		return getHibernateTemplate().findByCriteria(criteria);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param criteria
	 * 
	 * @param firstResult
	 * 
	 * @param maxResults
	 * 
	 * @return
	 * 
	 * @see
	 * com.my.persistence.Persistence#findByCriteria(org.hibernate.criterion
	 * .DetachedCriteria, int, int)
	 */
	@Override
	public List findByCriteria(DetachedCriteria criteria, int firstResult,
			int maxResults) {
		return getHibernateTemplate().findByCriteria(criteria, firstResult,
				maxResults);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @param propertyNames
	 * 
	 * @return
	 * 
	 * @see
	 * com.my.persistence.Persistence#findEqualByEntity(java.io.Serializable,
	 * java.lang.String[])
	 */
	@Override
	public List<T> findEqualByEntity(T entity, String[] propertyNames) {
		Criteria criteria = this.createCriteria();
		Example exam = Example.create(entity);
		exam.excludeZeroes();
		String[] defPropertys = getSessionFactory().getClassMetadata(
				entityClass).getPropertyNames();
		for (String defProperty : defPropertys) {
			int ii = 0;
			for (ii = 0; ii < propertyNames.length; ++ii) {
				if (defProperty.equals(propertyNames[ii])) {
					criteria.addOrder(Order.asc(defProperty));
					break;
				}
			}
			if (ii == propertyNames.length) {
				exam.excludeProperty(defProperty);
			}
		}
		criteria.add(exam);
		return (List<T>) criteria.list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @param propertyNames
	 * 
	 * @return
	 * 
	 * @see
	 * com.my.persistence.Persistence#findLikeByEntity(java.io.Serializable,
	 * java.lang.String[])
	 */
	@Override
	public List<T> findLikeByEntity(T entity, String[] propertyNames) {
		Criteria criteria = this.createCriteria();
		for (String property : propertyNames) {
			try {
				Object value = PropertyUtils.getProperty(entity, property);
				if (value instanceof String) {
					criteria.add(Restrictions.like(property, (String) value,
							MatchMode.ANYWHERE));
					criteria.addOrder(Order.asc(property));
				} else {
					criteria.add(Restrictions.eq(property, value));
					criteria.addOrder(Order.asc(property));
				}

			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}

		}

		return (List<T>) criteria.list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param criteria
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#getRowCount(org.hibernate.criterion.
	 * DetachedCriteria)
	 */
	@Override
	public Long getRowCount(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());// Projections.rowCount()
														// 表示计算row数
		List list = this.findByCriteria(criteria, 0, 1);// 只查询1条，0到1
		return (Long) list.get(0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param criteria
	 * 
	 * @param propertyName
	 * 
	 * @param StatName
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#getStatValue(org.hibernate.criterion.
	 * DetachedCriteria, java.lang.String, java.lang.String)
	 */
	@Override
	public Object getStatValue(DetachedCriteria criteria, String propertyName,
			String StatName) {
		if (StatName.toLowerCase().equals("max")) {
			criteria.setProjection(Projections.max(propertyName));
		} else if (StatName.toLowerCase().equals("min")) {
			criteria.setProjection(Projections.min(propertyName));
		} else if (StatName.toLowerCase().equals("avg")) {
			criteria.setProjection(Projections.avg(propertyName));
		} else if (StatName.toLowerCase().equals("sum")) {
			criteria.setProjection(Projections.sum(propertyName));
		} else {
			return null;
		}
		List list = this.findByCriteria(criteria, 0, 1);
		return list.get(0);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @param lock
	 * 
	 * @see com.my.persistence.Persistence#lock(java.io.Serializable,
	 * org.hibernate.LockMode)
	 */
	@Override
	public void lock(T entity, LockMode lockMode) {
		getHibernateTemplate().lock(entity, lockMode);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param proxy
	 * 
	 * @see com.my.persistence.Persistence#initialize(java.lang.Object)
	 */
	@Override
	public void initialize(Object proxy) {
		getHibernateTemplate().initialize(proxy);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.my.persistence.Persistence#flush()
	 */
	@Override
	public void flush() {
		getHibernateTemplate().flush();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @see com.my.persistence.Persistence#delete(java.io.Serializable)
	 */
	@Override
	public void delete(T entity) {
		getHibernateTemplate().delete(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @param lock
	 * 
	 * @see com.my.persistence.Persistence#deleteWithLock(java.io.Serializable,
	 * org.hibernate.LockMode)
	 */
	@Override
	public void deleteWithLock(T entity, LockMode lockMode) {
		getHibernateTemplate().delete(entity, lockMode);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @param values
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#find(java.lang.String,
	 * java.lang.Object[])
	 */
	@Override
	public List find(String queryString, Object[] values) {
		return getHibernateTemplate().find(queryString, values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryName
	 * 
	 * @param values
	 * 
	 * @return
	 * 
	 * @see com.my.persistence.Persistence#findByNamedQuery(java.lang.String,
	 * java.lang.Object[])
	 */
	@SuppressWarnings("rawtypes")
	@Override
	public List findByNamedQuery(String queryName, Object[] values) {
		return getHibernateTemplate().findByNamedQuery(queryName, values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryName
	 * 
	 * @param paramNames
	 * 
	 * @param values
	 * 
	 * @return
	 * 
	 * @see
	 * com.my.persistence.Persistence#findByNamedQueryAndNamedParam(java.lang
	 * .String, java.lang.String[], java.lang.Object[])
	 */
	@Override
	public List findByNamedQueryAndNamedParam(String queryName,
			String[] paramNames, Object[] values) {
		return getHibernateTemplate().findByNamedQueryAndNamedParam(queryName,
				paramNames, values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param hql
	 * 
	 * @param offset
	 * 
	 * @param length
	 * 
	 * @return
	 * 
	 * @see
	 * com.malamute.core.persistence.Persistence#getListForPage(java.lang.String
	 * , int, int)
	 */
	@Override
	public List getListForPage(final String hql, final int offset,
			final int length) {
		List list = getHibernateTemplate().executeFind(new HibernateCallback() {
			@Override
			public Object doInHibernate(org.hibernate.Session session)
					throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				query.setFirstResult(offset);
				query.setMaxResults(length);
				List list = query.list();
				return list;

			}
		});
		return list;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @param criterions
	 * 
	 * @param offset
	 * 
	 * @param length
	 * 
	 * @return
	 * 
	 * @see
	 * com.malamute.core.persistence.Persistence#getListForPage(java.io.Serializable
	 * , org.hibernate.criterion.Criterion[], int, int)
	 */
	@Override
	public List getListForPage(final Class entity,
			final Criterion[] criterions, final int offset, final int length) {
		// TODO 这里需要修改下？
		List list = getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(org.hibernate.Session session) {
				Criteria criteria = session.createCriteria(entity);
				// 循环遍历添加约束条件

				for (int i = 0; i < criterions.length; i++) {
					criteria.add(criterions[i]);
				}
				criteria.setFirstResult(offset);
				criteria.setMaxResults(length);
				return criteria.list();
			}
		});
		return list;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param queryString
	 * 
	 * @param page
	 * 
	 * @see
	 * com.malamute.core.hibernate.HibernateBaseDao#listByPage(java.lang.String,
	 * com.malamute.core.common.Page)
	 */
	@Override
	public void listByPage(String queryString, Page page) {
		// TODO 这里有疑问
		// Criterion[] criteria = null;
		// DetachedCriteria dCriteria = DetachedCriteria.forEntityName(entity
		// .getName());
		// if (criteria != null) {
		// for (int i = 0; i < criteria.length; i++) {
		// dCriteria.add(criteria[i]);
		// }
		// }
		List list = this.getListForPage(queryString, page.getCurrentPageNum()
				* page.getPageSize(), page.getPageSize());

		// List list = persistence.getListForPage(entity, criteria,
		// page.getPageSize() * page.getCurrentPageNum(),
		// page.getPageSize());
		page.setList(list);
		// page.setTotalPageNum(persistence.getRowCount(dCriteria));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param criteria
	 * 
	 * @param page
	 * 
	 * @see
	 * com.malamute.core.hibernate.HibernateBaseDao#listByPage(org.hibernate
	 * .criterion.DetachedCriteria, com.malamute.core.common.Page)
	 */
	@Override
	public void listByPage(DetachedCriteria criteria, Page page) {
		List list = this.findByCriteria(criteria, page.getCurrentPageNum()
				* page.getPageSize(), page.getPageSize());
		page.setTotalNum(this.getRowCount(criteria));
		page.setList(list);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param criterions
	 * 
	 * @param entity
	 * 
	 * @param page
	 * 
	 * @see
	 * com.malamute.core.hibernate.HibernateBaseDao#listByPage(org.hibernate
	 * .criterion.Criterion[], java.lang.Class, com.malamute.core.common.Page)
	 */
	@Override
	public void listByPage(Criterion[] criterions, Class entity, Page page) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @param entity
	 * 
	 * @return
	 * 
	 * @see
	 * com.malamute.core.hibernate.HibernateBaseDao#getAllForTree(java.lang.
	 * Class)
	 */
	@Override
	public List listForTree(Class entity) {
		DetachedCriteria criteria = DetachedCriteria.forClass(entity);
		List list = this.findByCriteria(criteria);
		return list;
	}
}
